Discourse 內建有 SSO 登入功能取名叫做 Discourse connect,要打開這個功能的話在 admin 頁面裡找到 enable discourse connect 功能並啟用。

Discourse connect 需要實作一個 discourse connect url 提供驗證,當嘗試登入時 Discourse 會開啟該 url 並帶有 sso 跟 sig 參數,然後經由以下步驟驗證登入。
discourse_connect_secret 作為密鑰對 PAYLOAD 進行 HMAC-SHA256 的運算結果等於 sig(sig 會是十六進制編碼的)。sso  解密後的參數執行所需的任何身份驗證。SiteSetting.auth_overrides_username 被設置。SiteSetting.auth_overrides_name 被設置。SiteSetting.discourse_connect_overrides_avatar 被設置。avatar_url 是否已更改。SiteSetting.discourse_connect_overrides_bio 被設置。discourse_connect_secret 作為密鑰,對 Base64 編碼的參數進行 HMAC-SHA256 的運算,輸出字串。sso 和 sig 參數的 return_sso_url(例如:http://discourse_site/session/sso_login?sso=payload&sig=sig)。上記步驟用 Typescript 實作的話會像這樣:
import crypto from 'crypto';
import { get } from 'lodash';
import qs from 'qs';
const sso =
  '{sso from discourse}'; //payload
const sig = '{sig from discourse}';
const discourse_connect_secret = '{the secret code}';
// decode the base64 encoded string
const decoded = Buffer.from(sso, 'base64').toString('utf-8');
console.log(decoded);
const params = qs.parse(decoded);
console.log(params);
const returnUrl = get(params, 'return_sso_url');
// Validate the signature: ensure that HMAC-SHA256 of PAYLOAD (using discourse_connect_secret, as the key) is equal to the sig (sig will be hex encoded).
const checkSig = crypto
  .createHmac('sha256', discourse_connect_secret)
  .update(sso)
  .digest('hex');
console.log(checkSig === sig);
const authPayload = {
  nonce: params.nonce,
  email: 'test@example.com',
  external_id: 1,
};
// Base64 encode payload
const returnSso = Buffer.from(qs.stringify(authPayload)).toString('base64');
// Calculate a HMAC-SHA256 hash of the returnSso using discourse_connect_secret as the key and Base64 encoded payload as text
const returnSig = crypto
  .createHmac('sha256', discourse_connect_secret)
  .update(returnSso)
  .digest('hex');
console.log({
  sso: returnSso,
  sig: returnSig,
});
// Get return url
console.log(returnUrl + '?' + qs.stringify({ sso: returnSso, sig: returnSig }));
使用這個功能,就能在主應用跟 Discourse 間同步用戶頭像跟名稱等資訊。
另外如果想在主應用登出時同步登出 Discourse 帳號,就要經由 Discourse API 。
步驟:
直接經由伺服器呼叫 Discourse API 會需要 api key,在 admin → API keys 頁面生成。
用主應用的用戶 id 查詢 Discourse 的用戶 id (對照上面的 external_id) :
GET {discourse_base_url}/u/by-external/:id.json
Respond:
{
	...
	user : {
		 id: 3
	}
}
登出 Discourse 用戶
POST {discourse_base_url}/admin/users/:id/log_out.json
Respond:
{
    "success": "OK"
}